/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.startup;


import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.ConnectException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.LogManager;

import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Server;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.security.SecurityConfig;
import org.apache.juli.ClassLoaderLogManager;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.Rule;
import org.apache.tomcat.util.digester.RuleSet;
import org.apache.tomcat.util.file.ConfigFileLoader;
import org.apache.tomcat.util.file.ConfigurationSource;
import org.apache.tomcat.util.log.SystemLogHandler;
import org.apache.tomcat.util.res.StringManager;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;


/**
 * Startup/Shutdown shell program for Catalina.  The following command line
 * options are recognized:
 * <ul>
 * <li><b>-config {pathname}</b> - Set the pathname of the configuration file
 *     to be processed.  If a relative path is specified, it will be
 *     interpreted as relative to the directory pathname specified by the
 *     "catalina.base" system property.   [conf/server.xml]</li>
 * <li><b>-help</b>      - Display usage information.</li>
 * <li><b>-nonaming</b>  - Disable naming support.</li>
 * <li><b>configtest</b> - Try to test the config</li>
 * <li><b>start</b>      - Start an instance of Catalina.</li>
 * <li><b>stop</b>       - Stop the currently running instance of Catalina.</li>
 * </ul>
 *
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 */
//其实 Catalina 也是 Tomcat 中的一个组件，它负责的是解析 Tomcat 的配置文件（server.xml），以此来创建服务器 Server 组件并进行管理
//因此也可以认为整个 Tomcat 就是一个 Catalina 实例，Tomcat 启动的时候会初始化这个实例，Catalina 实例通过加载server.xml 完成其他实例的创建，
//创建并管理一个 Server，Server 创建并管理多个服务， 每个服务又可以有多个Connector 和一个 Container。
public class Catalina {


    /**
     * The string manager for this package.
     */
    protected static final StringManager sm =
        StringManager.getManager(Constants.Package);

    public static final String SERVER_XML = "conf/server.xml";

    // ----------------------------------------------------- Instance Variables

    /**
     * Use await.
     * 用于await的flag
     */
    protected boolean await = false;

    /**
     * Pathname to the server configuration file.
     * Server配置的文件路
     */
    protected String configFile = SERVER_XML;

    // XXX Should be moved to embedded
    /**
     * The shared extensions class loader for this server.
     * 此server的shared类加载器
     */
    protected ClassLoader parentClassLoader =
        Catalina.class.getClassLoader();


    /**
     * The server component we are starting or stopping.
     *  Server组件
     */
    protected Server server = null;


    /**
     * Use shutdown hook flag.
     * 使用shutdown钩子的flag
     */
    protected boolean useShutdownHook = true;


    /**
     * Shutdown hook.
     * Shutdown钩子实例
     */
    protected Thread shutdownHook = null;


    /**
     * Is naming enabled ?
     * 默认需要开启Naming
     */
    protected boolean useNaming = true;


    /**
     * Prevent duplicate loads.
     * 预防重复加载的标记字段
     */
    protected boolean loaded = false;


    /**
     * Rethrow exceptions on init failure.
     */
    protected boolean throwOnInitFailure =
            Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE");


    /**
     * Generate Tomcat embedded code from configuration files.
     * 根据配置文件生成Tomcat内嵌代码[通过启动命令行参数进行赋值]
     * 这个属性指示是否生成代码来处理特定的配置项。当generateCode设置为true时，解析器会在运行时动态地生成代码来处理指定的配置项。这对于某些需要灵活处理的配置项很有用
     */
    protected boolean generateCode = false;


    /**
     * Location of generated sources.
     */
    protected File generatedCodeLocation = null;


    /**
     * Value of the argument.
     */
    protected String generatedCodeLocationParameter = null;


    /**
     * Top package name for generated source.
     */
    protected String generatedCodePackage = "catalinaembedded";


    /**
     * Use generated code as a replacement for configuration files.
     * [通过启动命令行参数进行赋值]
     * 这个属性指示是否使用生成的代码来处理特定的配置项。当useGeneratedCode设置为true时，
     * 解析器会使用预生成的代码来处理指定的配置项，而不是手动编写处理逻辑。这可以提高解析效率并简化代码
     */
    protected boolean useGeneratedCode = false;


    // ----------------------------------------------------------- Constructors

    public Catalina() {
        setSecurityProtection();
        ExceptionUtils.preload();
    }


    // ------------------------------------------------------------- Properties

    public void setConfigFile(String file) {
        configFile = file;
    }


    public String getConfigFile() {
        return configFile;
    }


    public void setUseShutdownHook(boolean useShutdownHook) {
        this.useShutdownHook = useShutdownHook;
    }


    public boolean getUseShutdownHook() {
        return useShutdownHook;
    }


    public boolean getGenerateCode() {
        return this.generateCode;
    }


    public void setGenerateCode(boolean generateCode) {
        this.generateCode = generateCode;
    }


    public boolean getUseGeneratedCode() {
        return this.useGeneratedCode;
    }


    public void setUseGeneratedCode(boolean useGeneratedCode) {
        this.useGeneratedCode = useGeneratedCode;
    }


    public File getGeneratedCodeLocation() {
        return this.generatedCodeLocation;
    }


    public void setGeneratedCodeLocation(File generatedCodeLocation) {
        this.generatedCodeLocation = generatedCodeLocation;
    }


    public String getGeneratedCodePackage() {
        return this.generatedCodePackage;
    }


    public void setGeneratedCodePackage(String generatedCodePackage) {
        this.generatedCodePackage = generatedCodePackage;
    }


    /**
     * @return <code>true</code> if an exception should be thrown if an error
     * occurs during server init
     */
    public boolean getThrowOnInitFailure() {
        return throwOnInitFailure;
    }


    /**
     * Set the behavior regarding errors that could occur during server init.
     * @param throwOnInitFailure the new flag value
     */
    public void setThrowOnInitFailure(boolean throwOnInitFailure) {
        this.throwOnInitFailure = throwOnInitFailure;
    }


    /**
     * Set the shared extensions class loader.
     *
     * @param parentClassLoader The shared extensions class loader.
     */
    public void setParentClassLoader(ClassLoader parentClassLoader) {
        this.parentClassLoader = parentClassLoader;
    }

    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null) {
            return parentClassLoader;
        }
        return ClassLoader.getSystemClassLoader();
    }

    public void setServer(Server server) {
        this.server = server;
    }


    public Server getServer() {
        return server;
    }


    /**
     * @return <code>true</code> if naming is enabled.
     */
    public boolean isUseNaming() {
        return this.useNaming;
    }


    /**
     * Enables or disables naming support.
     *
     * @param useNaming The new use naming value
     */
    public void setUseNaming(boolean useNaming) {
        this.useNaming = useNaming;
    }

    public void setAwait(boolean b) {
        await = b;
    }

    public boolean isAwait() {
        return await;
    }

    // ------------------------------------------------------ Protected Methods


    /**
     * Process the specified command line arguments.
     *
     * @param args Command line arguments to process
     * @return <code>true</code> if we should continue processing
     */
    protected boolean arguments(String args[]) {

        boolean isConfig = false;
        boolean isGenerateCode = false;

        if (args.length < 1) {
            usage();
            return false;
        }

        for (String arg : args) {
            if (isConfig) {
                configFile = arg;
                isConfig = false;
            } else if (arg.equals("-config")) {
                isConfig = true;
            //是否生成配置项代码
            } else if (arg.equals("-generateCode")) {
                setGenerateCode(true);
                isGenerateCode = true;
            //是否使用生成的配置项代码
            } else if (arg.equals("-useGeneratedCode")) {
                setUseGeneratedCode(true);
                isGenerateCode = false;
            } else if (arg.equals("-nonaming")) {
                setUseNaming(false);
                isGenerateCode = false;
            } else if (arg.equals("-help")) {
                usage();
                return false;
            } else if (arg.equals("start")) {
                isGenerateCode = false;
                // NOOP
            } else if (arg.equals("configtest")) {
                isGenerateCode = false;
                // NOOP
            } else if (arg.equals("stop")) {
                isGenerateCode = false;
                // NOOP
            } else if (isGenerateCode) {
                generatedCodeLocationParameter = arg;
                isGenerateCode = false;
            } else {
                usage();
                return false;
            }
        }

        return true;
    }


    /**
     * Return a File object representing our configuration file.
     * @return the main configuration file
     */
    protected File configFile() {

        File file = new File(configFile);
        //是否为绝对路径
        if (!file.isAbsolute()) {
            //将tomcat工作目录作为其路径前缀
            file = new File(Bootstrap.getCatalinaBase(), configFile);
        }
        //此时的file为G:\tomcat\apache-tomcat-10.1.2-src\home\conf\server.xml
        return file;

    }


    /**
     * Create and configure the Digester we will be using for startup.
     * @return the main digester to parse server.xml
     */
    protected Digester createStartDigester() {
        // Initialize the digester
        // 创建Digester对象()
        Digester digester = new Digester();
        //是否对xml文档进行类似XSD等类型的校验，默认为fasle。
        digester.setValidating(false);
        //是否进行节点设置规则校验，如果xml中相应节点没有设置解析规则会在控制台显示提示信息。
        digester.setRulesValidation(true);
        // 这儿设置无效的属性，fake是赝品的意思，也就是在检查到这些属性直接认为是无效的 将XML节点中的className作为假属性，不必调用默认的setter方法
        //(一般的节点属性在解析时会以属性作为入参调用该节点相应对象的setter方法，而className属性的作用提示解析器使用该属性的值作来实例化对象)
        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        // Ignore className on all elements
        List<String> objectAttrs = new ArrayList<>();
        //注意这里设置的无效属性只是不调用setter方法而已，在创建实例对象的时候还是需要使用的
        objectAttrs.add("className");
        fakeAttributes.put(Object.class, objectAttrs);
        // Ignore attribute added by Eclipse for its internal tracking
        List<String> contextAttrs = new ArrayList<>();
        contextAttrs.add("source");
        fakeAttributes.put(StandardContext.class, contextAttrs);
        // Ignore Connector attribute used internally but set on Server
        List<String> connectorAttrs = new ArrayList<>();
        //portOffset:嵌套连接器的端口偏移量，为非负数，默认为0
        connectorAttrs.add("portOffset");
        fakeAttributes.put(Connector.class, connectorAttrs);
        digester.setFakeAttributes(fakeAttributes);
        //使用当前线程的上下文类加载器，主要加载FactoryCreateRule(没有无参构造)和ObjectCreateRule(默认无参构造)
        digester.setUseContextClassLoader(true);
        /*
         * pattern, 匹配的节点
         * className, 该节点对应的默认实体类(一般是系统提供的)
         * attributeName, 如果该节点有attributeName属性, 用attributeName属性对应的值替换默认实体类（是指标签中的className属性，该属性指向其实现类）
         */
        // Configure the actions we will be using
        //增加对象创建规则，当匹配到pattern模式时，如果指定了attributeName，则根据attributeName创建类对象；否则根据className创建类对象
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        // 增加属性设置规则，当匹配到pattern模式时，就填充其属性
        digester.addSetProperties("Server");
        /*
         * Digester.addSetNext(String pattern, String methodName, String paramType)
         * pattern, 匹配的节点
         * methodName, 调用父节点的方法
         * paramType, 父节点的方法接收的参数类型
         * Digester匹配到server节点，将调用catalina(Server的父节点)的setServer方法，参数为Server对象(注意此处只是设置规则还未解析，在解析之前会把catalina放入到栈集合中)
         */
        //增加设置下一个规则，当匹配到pattern模式时，调用父节点的methodName方法，paramType为方法传入参数的类型
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        //为Server添加全局J2EE企业命名上下文
        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResourcesImpl");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResourcesImpl");

        //为Server添加生命周期监听器   addRule:当匹配到pattern模式时，增加一个自定义规则
        //当匹配到”Server/Listener”节点，使用ListenerCreateRule解析。注意ListenerCreateRule第一个参数是className，即默认类名，为null。
        // 所以Listener不会有默认值类型。因为Listener本来就是自定义的  ListenerCreateRule为ObjectCreateRule的子类
        digester.addRule("Server/Listener",
                new ListenerCreateRule(null, "className"));
        digester.addSetProperties("Server/Listener");
        //而addLifecycleListener方法会把对应的LifecycleListener添加到LifecycleBase中的目标集合中
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");
        //创建Service实例
        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");
        //为Service添加生命周期监听器 此时父级就是Service
        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        // Executor
        // 创建StandardThreadExecutor实例，并调用父节点Service的addExecutor方法添加到Service。
        // 通过该配置我们可以知道，Tomcat共享Excetor的级别为Service，Catalina默认情况下未配置Executor，即不共享
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor",
                            "addExecutor",
                            "org.apache.catalina.Executor");
        // 创建Connector实例，并调用父组件Service的addConnector方法添加到Service。设置相关属性时，将executor和sslImplementationName属性排除，
        // 因为在Connector创建时，会判断当前是否指定了executor属性，如果是，则从Service中查找该名称的executor并设置到Connector中。同样，Connector创建时，
        // 也会判断是否添加了sslIlplementationName属性，如果是，则将属性值设置到使用的协议中，为其指定一个SSL实现，该过程是在ConnectorCreateRule中的begin中进行的，
        // 要在属性规则中的begin方法之前执行，所以属性规则赋值的时候要进行排除操作
        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());
        digester.addSetProperties("Server/Service/Connector",
                new String[]{"executor", "sslImplementationName", "protocol"});
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");
        //该规则将server中设置的portOffset设置到Connector中
        digester.addRule("Server/Service/Connector", new AddPortOffsetRule());
        // 为Connector添加虚拟主机SSL配置
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                                 "org.apache.tomcat.util.net.SSLHostConfig");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig",
                "addSslHostConfig",
                "org.apache.tomcat.util.net.SSLHostConfig");

        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                         new CertificateCreateRule());
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/Certificate", new String[]{"type"});
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                            "addCertificate",
                            "org.apache.tomcat.util.net.SSLHostConfigCertificate");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                                 "org.apache.tomcat.util.net.openssl.OpenSSLConf");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                            "setOpenSslConf",
                            "org.apache.tomcat.util.net.openssl.OpenSSLConf");

        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                                 "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                            "addCmd",
                            "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
        // 为Connector添加生命周期监听器
        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");
        //为Connector添加升级协议(用于支持HTTP/2)
        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                                  null, // MUST be specified in the element
                                  "className");
        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
        digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                            "addUpgradeProtocol",
                            "org.apache.coyote.UpgradeProtocol");

        // Add RuleSets for nested elements
        // addRuleSet:增加规则集，一个规则集指的是对一个节点及下面的所有子节点（子节点、子节点的子节点…）的解析
        // 此部分指定了Servlet容器相关的各级嵌套子节点的解析规则，而且每类嵌套子节点的解析规则封装为一个RuleSet，
        // 包括GlobalNamingResources，Engine，Host，Context以及Cluster解析，最终会调用每个RuleSet中的addRuleInstances方法，增加各自的解析规则
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        // Catalina的Context配置并非来源一处，这里仅指server.xml中配置。多数情况下，并不需要在server.xml中配置Context，而是由HostConfig(host中的监听器)自动扫描部署目录，
        // 以context.xml文件为基础进行解析创建，如果通过IDE启动Tomcat并部署应用，其Context配置将会被动态更新到server.xml中
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        // When the 'engine' is found, set the parentClassLoader.
        // 我们在初始化3个类加载器后，将shareClassLoad赋值给容器中的parentClassLoader属性
        // 在之后创建完webappsClassLoad就可以通过该属性获取到shareClassLoad类加载器，从而可以使两者绑定父子关系
        digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(parentClassLoader));
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

        return digester;

    }

    /**
     * Cluster support is optional. The JARs may have been removed.
     */
    private void addClusterRuleSet(Digester digester, String prefix) {
        Class<?> clazz = null;
        Constructor<?> constructor = null;
        try {
            clazz = Class.forName("org.apache.catalina.ha.ClusterRuleSet");
            constructor = clazz.getConstructor(String.class);
            RuleSet ruleSet = (RuleSet) constructor.newInstance(prefix);
            digester.addRuleSet(ruleSet);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("catalina.noCluster",
                        e.getClass().getName() + ": " +  e.getMessage()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("catalina.noCluster",
                        e.getClass().getName() + ": " +  e.getMessage()));
            }
        }
    }

    /**
     * Create and configure the Digester we will be using for shutdown.
     * @return the digester to process the stop operation
     * 只需要定义server相应的规则即可，因为tomcat的停止是由server监听负责
     */
    protected Digester createStopDigester() {

        // Initialize the digester
        Digester digester = new Digester();
        digester.setUseContextClassLoader(true);

        // Configure the rules we need for shutting down
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        return digester;

    }

    /* Java解析xml主要由DOM4J（一次读取到内存并解析）、SAX（一次解析一部分）
     （1）预加载DOM树：将整个XML文件读取到内存中，在内容中构造一个DOM树，也叫对象模型集合，然后java代码只需要操作这个树就可以。
        优点：使用DOM时，XML文档的结构在内存中很清晰，元素与元素之间的关系保留了下来。即能记录文档的所有信息。
        缺点：如果XML文档非常大，把整个XML文档装在进内存容易造成内容溢出，无法加载了。
     （2）事件机制的SAX：一行一行的读取XML文件，每遇到一个节点，就看看有没有监听该事件的监听器，如果有就触发。当XML文件读取结束后，内存里不会保存任何数据，整个XML的解析就结束了。
        所以，这里面最重要的是对感兴趣的节点进行监听和处理。
        优点：使用SAX不会占用大量内存来保存XML文档数据，效率高。
        缺点：当解析一个元素时，上一个元素的信息已经丢弃，也就是说没有保存元素与元素之间的结构关系。
    * digester本身采用SAX的解析方式，并提供了一层包装，对使用者更加友好，后来独立出来成为apache的Commons下面的单独的子项目,但是tomcat中没有使用jar包的方式引入而是
    * 直接把digester的源码放到项目的一个目录下了   digester底层是基于SAX+事件驱动+栈的方式来搭建实现的：
    * SAX，用于解析xml
    * 事件驱动，在SAX解析的过程中加入事件来支持我们的对象映射（例如当SAX读取到一个元素的开始时，会通知用户当前解析到一个元素的开始标签。而用户可以在整个解析的过程中完成自己的业务逻辑，
    * 而digester则利用该机制通过实现SAX的基础类DefaultHandler来实现事件驱动  从而调用每个pattern（标签名称）的rule执行链中的阶段方法，继而创建标签对应的实例对象，属性赋值等）
    * 栈，确保XML上不同的节点有依赖关系,每遇到一个新的节点时，把这个对象把它压入栈(push)，节点解析结束时，可以再从栈中弹出(pop)，栈里面最顶部的对象永远都是现在正在解析的对象，
    * 第二个是它的父节点，提供API调用父节点的方法把引用当前对象，这样就解决了依赖的问题
    */
    protected void parseServerXml(boolean start) {
        // Set configuration source
        // 将我们默认的配置文件路径和tomcat实例工作目录封装到CatalinaBaseConfigurationSource对象中 设置到ConfigFileLoader中的source属性中
        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
        //server.xml文件作为一个输入流传入
        File file = configFile();
        // useGeneratedCode和generateCode 使用生成的代码来替代配置文件，默认情况下两者皆为false
        // useGeneratedCode属性是在generateCode设置为true的情况下才会生效，用于决定是否使用生成的代码来处理特定的配置项  两者一个是用来生成代码，一个是用来决定是否使用生成的代码
        // 这样的设计使得生成代码和使用生成代码的过程可以分开进行,增加了灵活性
        if (useGeneratedCode && !Digester.isGeneratedCodeLoaderSet()) {
            // Load loader
            // 如果使用生成的代码那么就需要初始化对应的类加载器
            String loaderClassName = generatedCodePackage + ".DigesterGeneratedCodeLoader";
            try {
                Digester.GeneratedCodeLoader loader = (Digester.GeneratedCodeLoader)
                        Catalina.class.getClassLoader().loadClass(loaderClassName).getDeclaredConstructor().newInstance();
                Digester.setGeneratedCodeLoader(loader);
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.info(sm.getString("catalina.noLoader", loaderClassName), e);
                } else {
                    log.info(sm.getString("catalina.noLoader", loaderClassName));
                }
                // No loader so don't use generated code
                useGeneratedCode = false;
            }
        }

        // Init source location
        // 初始化server.xml的位置
        File serverXmlLocation = null;
        String xmlClassName = null;
        if (generateCode || useGeneratedCode) {
            //catalinaembedded.ServerXml类用于配置Tomcat服务器的server.xml文件。通过使用该类，可以以编程方式配置Tomcat服务器的各种组件
            //catalinaembedded.ServerXmlStop类是用于停止一个已经启动的Tomcat服务器的工具类
            xmlClassName = start ? generatedCodePackage + ".ServerXml" : generatedCodePackage + ".ServerXmlStop";
        }
        if (generateCode) {
            if (generatedCodeLocationParameter != null) {
                generatedCodeLocation = new File(generatedCodeLocationParameter);
                if (!generatedCodeLocation.isAbsolute()) {
                    generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), generatedCodeLocationParameter);
                }
            } else {
                generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), "work");
            }
            serverXmlLocation = new File(generatedCodeLocation, generatedCodePackage);
            if (!serverXmlLocation.isDirectory() && !serverXmlLocation.mkdirs()) {
                log.warn(sm.getString("catalina.generatedCodeLocationError", generatedCodeLocation.getAbsolutePath()));
                // Disable code generation
                generateCode = false;
            }
        }
        // 用 SAXParser来解析xml，解析完了之后，xml里定义的各种标签就有对应的实现类对象了
        ServerXml serverXml = null;
        if (useGeneratedCode) {
            serverXml = (ServerXml) Digester.loadGeneratedClass(xmlClassName);
        }

        if (serverXml != null) {
            serverXml.load(this);
        } else {
            //将server.xml转化为对应的resource对象
            try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
                // Create and execute our Digester
                // 创建解析器，配置相应的规则 为解析xml文件提供基础
                Digester digester = start ? createStartDigester() : createStopDigester();
                //server.xml文件作为一个输入流传入
                InputStream inputStream = resource.getInputStream();
                //使用inputStream构造一个sax的inputSource
                InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
                inputSource.setByteStream(inputStream);
                //把当前类catalina压入到digester的栈顶，用来作为digester解析出来的对象的一种引用(换句话说就是server的父类)
                digester.push(this);
                if (generateCode) {
                    digester.startGeneratingCode();
                    generateClassHeader(digester, start);
                }
                //调用digester的parse()方法进行解析
                digester.parse(inputSource);
                if (generateCode) {
                    generateClassFooter(digester);
                    try (FileWriter writer = new FileWriter(new File(serverXmlLocation,
                            start ? "ServerXml.java" : "ServerXmlStop.java"))) {
                        writer.write(digester.getGeneratedCode().toString());
                    }
                    digester.endGeneratingCode();
                    Digester.addGeneratedClass(xmlClassName);
                }
            } catch (Exception e) {
                log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
                if (file.exists() && !file.canRead()) {
                    log.warn(sm.getString("catalina.incorrectPermissions"));
                }
            }
        }
    }

    public void stopServer() {
        stopServer(null);
    }

    public void stopServer(String[] arguments) {
        //1.参数设置
        if (arguments != null) {
            arguments(arguments);
        }

        Server s = getServer();
        //2.如果Server存在，就调用Server的stop和destroy方法进行关闭
        if (s == null) {
            parseServerXml(false);
            if (getServer() == null) {
                log.error(sm.getString("catalina.stopError"));
                System.exit(1);
            }
        } else {
            // Server object already present. Must be running as a service
            try {
                s.stop();
                s.destroy();
            } catch (LifecycleException e) {
                log.error(sm.getString("catalina.stopError"), e);
            }
            return;
        }

        // Stop the existing server
        // 3.如果Server不存在，就重新解析server.xml文件构造server，然后通过socket发送shutdown命令关闭
        s = getServer();
        if (s.getPortWithOffset() > 0) {
            try (Socket socket = new Socket(s.getAddress(), s.getPortWithOffset());
                    OutputStream stream = socket.getOutputStream()) {
                String shutdown = s.getShutdown();
                for (int i = 0; i < shutdown.length(); i++) {
                    stream.write(shutdown.charAt(i));
                }
                stream.flush();
            } catch (ConnectException ce) {
                log.error(sm.getString("catalina.stopServer.connectException", s.getAddress(),
                        String.valueOf(s.getPortWithOffset()), String.valueOf(s.getPort()),
                        String.valueOf(s.getPortOffset())));
                log.error(sm.getString("catalina.stopError"), ce);
                System.exit(1);
            } catch (IOException e) {
                log.error(sm.getString("catalina.stopError"), e);
                System.exit(1);
            }
        } else {
            log.error(sm.getString("catalina.stopServer"));
            System.exit(1);
        }
    }


    /**
     * Start a new server instance.
     * 创建一个新的服务实例
     */
    public void load() {
        //避免二次执行
        if (loaded) {
            return;
        }
        loaded = true;

        long t1 = System.nanoTime();

        // Before digester - it may be needed
        //设置catalina.useNaming的系统参数(Naming提供JNDI服务)
        initNaming();

        // Parse main server.xml
        // 用digester解析server.xml文件，把配置文件中的配置解析成java对象，并且在解析过程中实例化了各Tomcat组件,实例化只到host这一层级
        // 因为context标签一般不在server.xml中定义，因为server.xml中改动的话需要重启tomcat才能生效
        // Context的实例化和启动过程比较特殊,在该方法中并没有实例化，是在Host组件启动时动态生成的
        parseServerXml(true);
        //在server.xml实例化的对象 可以通过Catalina对象层层递归找到  绑定了对应的父子级关系
        Server s = getServer();
        if (s == null) {
            return;
        }
        //将当前catalina对象设置到server组件属性中
        getServer().setCatalina(this);
        //tomcat的安装目录设置到server组件属性中
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        //tomcat的工作目录设置到server组件属性中
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        //替换掉System.out, System.err为自定义的PrintStream
        initStreams();

        // Start the new server
        try {
            //执行server的init方法，初始化Tomcat各组件(Host及其下级组件的初始化比较特殊，并没有在Engine的初始化方法中完成Host及下级组件的初始化)
            getServer().init();
        } catch (LifecycleException e) {
            if (throwOnInitFailure) {
                throw new java.lang.Error(e);
            } else {
                log.error(sm.getString("catalina.initError"), e);
            }
        }

        if(log.isInfoEnabled()) {
            log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
        }
    }


    /*
     * Load using arguments
     */
    public void load(String args[]) {

        try {
            if (arguments(args)) {
                load();
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }


    /**
     * Start a new server instance.
     */
    public void start() {
        //正常执行到此处时server组件已完成初始化，如果获取不到，则直接调用load，保证load方法先执行（解决CPU指令重排序带来的问题）
        if (getServer() == null) {
            load();
        }

        if (getServer() == null) {
            log.fatal(sm.getString("catalina.noServer"));
            return;
        }

        long t1 = System.nanoTime();

        // Start the new server
        //执行Server的start()方法，如果执行失败，就调用Server的destroy()方法
        try {
            //执行server的start方法
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                //如果start失败，就调用server的destroy方法,然后将子组件逐层销毁
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        if (log.isInfoEnabled()) {
            log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
        }
        //是否根据配置文件生成Tomcat内嵌代码，默认为false
        if (generateCode) {
            // Generate loader which will load all generated classes
            generateLoader();
        }

        // JVM既可以正常关闭，也可以强行关闭。正常关闭的触发方式有多种，包括：当最后一个“正常（非守护）”线程结束时，或者当调用了System.exit时，
        // 或者通过其他特定于平台的方法关闭时（例如发送了SIGINT信号或者键入Ctrl-C）。(https://www.pdai.tech/md/framework/tomcat/tomcat-x-catalina.html)
        // 在正常关闭中，JVM首先调用所有已注册的关闭钩子。JVM并不能保证关闭钩子的调用顺序。在关闭应用程序线程时，如果有（守护或者非守护）线程仍然在执行，
        // 那么这些线程接下来将与关闭进程并发执行。当所有的关闭钩子都执行结束时，如果runFinalizersOnExit为true【通过Runtime.runFinalizersOnExit(true)设置】，
        // 那么JVM将运行这些Finalizer（对象重写的finalize方法,在钩子中定义实现），然后再停止。JVM不会停止或中断任何在关闭时仍然运行的应用程序线程。当JVM最终结束时，这些线程将被强行结束。
        // 如果关闭钩子或者Finalizer没有执行完成，那么正常关闭进程“挂起”并且JVM必须被强行关闭。当JVM被强行关闭时，只是关闭JVM，并不会运行关闭钩子（举个例子，类似于电源都直接拔了，还怎么做其它动作呢？）

        // Register shutdown hook
        // 注册一个shutdown的钩子(关闭钩子是指通过Runtime.addShutdownHook注册的但尚未开始的线程(这些钩子都实现了thread)。这些钩子可以用于实现服务或者应用程序的清理工作)
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            //向jvm中注册了一个关闭钩子（当jvm关闭时会调用这些钩子中run方法）
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            // 如果正在使用JULI(Tomcat自带输出log的方法)，请禁用JULI的关机挂钩，因为关机挂钩并行运行，如果JULI的挂钩在CatalinaShutdownHook（）之前完成，
            // 日志消息可能会丢失
            LogManager logManager = LogManager.getLogManager();
            //默认情况下不使用，如需使用需要做一些配置
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }
        // 等待处理如何停止的问题 Bootstrap中daemon.setAwait(true)已将其设置为true
        if (await) {
            //await方法的作用是停住主线程，等待用户输入shutdown命令之后，停止等待，之后main线程就继续调用下面的stop方法来停止Tomcat。
            await();
            stop();
        }
    }


    /**
     * Stop an existing server instance.
     */
    public void stop() {

        try {
            // Remove the ShutdownHook first so that server.stop()
            // doesn't get invoked twice
            //将jvm中的钩子信息移除且还原
            if (useShutdownHook) {
                Runtime.getRuntime().removeShutdownHook(shutdownHook);

                // If JULI is being used, re-enable JULI's shutdown to ensure
                // log messages are not lost
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                            true);
                }
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
            // fine without the shutdown hook.
        }

        // Shut down the server
        try {
            Server s = getServer();
            LifecycleState state = s.getState();
            if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
                    && LifecycleState.DESTROYED.compareTo(state) >= 0) {
                // Nothing to do. stop() was already called
            } else {
                s.stop();
                s.destroy();
            }
        } catch (LifecycleException e) {
            log.error(sm.getString("catalina.stopError"), e);
        }

    }


    /**
     * Await and shutdown.
     */
    public void await() {

        getServer().await();

    }


    /**
     * Print usage information for this application.
     */
    protected void usage() {

        System.out.println(sm.getString("catalina.usage"));

    }


    protected void initStreams() {
        // Replace System.out and System.err with a custom PrintStream
        System.setOut(new SystemLogHandler(System.out));
        System.setErr(new SystemLogHandler(System.err));
    }


    protected void initNaming() {
        // Setting additional variables
        if (!useNaming) {
            log.info(sm.getString("catalina.noNaming"));
            System.setProperty("catalina.useNaming", "false");
        } else {
            System.setProperty("catalina.useNaming", "true");
            String value = "org.apache.naming";
            String oldValue =
                System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
            if (oldValue != null) {
                value = value + ":" + oldValue;
            }
            System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
            if( log.isDebugEnabled() ) {
                log.debug("Setting naming prefix=" + value);
            }
            value = System.getProperty
                (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
            if (value == null) {
                System.setProperty
                    (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
                     "org.apache.naming.java.javaURLContextFactory");
            } else {
                log.debug("INITIAL_CONTEXT_FACTORY already set " + value );
            }
        }
    }


    /**
     * Set the security package access/protection.
     */
    protected void setSecurityProtection(){
        SecurityConfig securityConfig = SecurityConfig.newInstance();
        securityConfig.setPackageDefinition();
        securityConfig.setPackageAccess();
    }


    protected void generateLoader() {
        String loaderClassName = "DigesterGeneratedCodeLoader";
        StringBuilder code = new StringBuilder();
        code.append("package ").append(generatedCodePackage).append(";").append(System.lineSeparator());
        code.append("public class ").append(loaderClassName);
        code.append(" implements org.apache.tomcat.util.digester.Digester.GeneratedCodeLoader {").append(System.lineSeparator());
        code.append("public Object loadGeneratedCode(String className) {").append(System.lineSeparator());
        code.append("switch (className) {").append(System.lineSeparator());
        for (String generatedClassName : Digester.getGeneratedClasses()) {
            code.append("case \"").append(generatedClassName).append("\" : return new ").append(generatedClassName);
            code.append("();").append(System.lineSeparator());
        }
        code.append("default: return null; }").append(System.lineSeparator());
        code.append("}}").append(System.lineSeparator());
        File loaderLocation = new File(generatedCodeLocation, generatedCodePackage);
        try (FileWriter writer = new FileWriter(new File(loaderLocation, loaderClassName + ".java"))) {
            writer.write(code.toString());
        } catch (IOException e) {
            // Should not happen
            log.debug("Error writing code loader", e);
        }
    }


    protected void generateClassHeader(Digester digester, boolean start) {
        StringBuilder code = digester.getGeneratedCode();
        code.append("package ").append(generatedCodePackage).append(";").append(System.lineSeparator());
        code.append("public class ServerXml");
        if (!start) {
            code.append("Stop");
        }
        code.append(" implements ");
        code.append(ServerXml.class.getName().replace('$', '.')).append(" {").append(System.lineSeparator());
        code.append("public void load(").append(Catalina.class.getName());
        code.append(' ').append(digester.toVariableName(this)).append(") {").append(System.lineSeparator());
    }


    protected void generateClassFooter(Digester digester) {
        StringBuilder code = digester.getGeneratedCode();
        code.append('}').append(System.lineSeparator());
        code.append('}').append(System.lineSeparator());
    }


    public interface ServerXml {
        public void load(Catalina catalina);
    }


    // --------------------------------------- CatalinaShutdownHook Inner Class

    // XXX Should be moved to embedded !
    /**
     * Shutdown hook which will perform a clean shutdown of Catalina if needed.
     */
    protected class CatalinaShutdownHook extends Thread {

        @Override
        public void run() {
            try {
                if (getServer() != null) {
                    //调用Catalina对象的stop方法来停止tomcat
                    Catalina.this.stop();
                }
            } catch (Throwable ex) {
                ExceptionUtils.handleThrowable(ex);
                log.error(sm.getString("catalina.shutdownHookFail"), ex);
            } finally {
                // If JULI is used, shut JULI down *after* the server shuts down
                // so log messages aren't lost
                //如果使用JULI日志，我们需要手动设置进行关闭 因为为了保证CatalinaShutdownHook先执行我们将JULI日志相关的钩子给关闭了
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).shutdown();
                }
            }
        }
    }


    private static final Log log = LogFactory.getLog(Catalina.class);


    /**
     * Rule that sets the parent class loader for the top object on the stack,
     * which must be a <code>Container</code>.
     */

    final class SetParentClassLoaderRule extends Rule {

        public SetParentClassLoaderRule(ClassLoader parentClassLoader) {

            this.parentClassLoader = parentClassLoader;

        }

        ClassLoader parentClassLoader = null;
        //在解析server.xml文件时，遇到<engin>标签时就会调用该方法来初始化父加载器属性值
        @Override
        public void begin(String namespace, String name, Attributes attributes)
            throws Exception {

            if (digester.getLogger().isDebugEnabled()) {
                digester.getLogger().debug("Setting parent class loader");
            }

            Container top = (Container) digester.peek();
            //该方法会将sharedLoader加载器设置到ContainerBase中的parentClassLoader属性中，相当于设置到容器上
            top.setParentClassLoader(parentClassLoader);

            StringBuilder code = digester.getGeneratedCode();
            if (code != null) {
                code.append(digester.toVariableName(top)).append(".setParentClassLoader(");
                code.append(digester.toVariableName(Catalina.this)).append(".getParentClassLoader());");
                code.append(System.lineSeparator());
            }
        }

    }

}
